#include "mmu.h"
#include "mutex.h"
#include "types.h"
#include "regs.h"
#include "print.h"

// kernel.ld中定义,是栈段结束地址
extern char end[];

// 页管理节点
struct page {
    struct page *next;
};

// 页管理器
struct {
    struct mutex lock;
    struct page *freelist;
} kmem;

// 释放一页内存
void os_kfree(void *addr)
{
    struct page* page;

    // 检查地址是否合法
    if(((u64)addr % PAGE_SIZE) != 0 || (char*)addr < end || (u64)addr >= KERN_STOP)
        panic("kfree");

    page = (struct page*)addr;

    // 将空闲页插入到链表头
    os_mutex_lock(&kmem.lock);
    page->next = kmem.freelist;
    kmem.freelist = page;
    os_mutex_unlock(&kmem.lock);
}

// 申请一页内存
void *os_kalloc(void)
{
    struct page* page;

    // 从链表头取出空闲页
    os_mutex_lock(&kmem.lock);
    page = kmem.freelist;
    if (page) {
        kmem.freelist = page->next;
    }
    os_mutex_unlock(&kmem.lock);

    return (void *)page;
}

// 内核页管理初始化
void os_kmem_init(void)
{
    char *p;
    char *pa_start = end;
    char *pa_end   = (char *)KERN_STOP;

    os_mutex_init(&kmem.lock, "kmem");

    // 初始化时将所有空闲内存构造为空闲链表
    p = (char*)PAGE_ROUNDUP((u64)pa_start);
    for(; p + PAGE_SIZE < (char*)pa_end; p += PAGE_SIZE) {
        os_kfree(p);
    }
}

void kmem_test(void)
{
    void *p1, *p2, *p3, *p4;

    p1 = os_kalloc();
    p2 = os_kalloc();
    os_kfree(p1);
    os_kfree(p2);
    p3 = os_kalloc();
    p4 = os_kalloc();
    os_kfree(p3);
    os_kfree(p4);

    if ((p3 == p2) && (p4 == p1)) {
        pr_test("kmem alloc & free test success.\n");
    } else {
        pr_test("kmem alloc & free test fail.\n");
        pr_test("     p1 = %p\n", p1);
        pr_test("     p2 = %p\n", p2);
        pr_test("     p3 = %p\n", p3);
        pr_test("     p4 = %p\n", p4);
    }
}
